﻿using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace BootstrapBlazorTest.Shared.Pages.Bind
{
    public partial class Page3
    {
        private class Foo
        {
            [Required]
            public string Name { get; set; }

            [Required]
            public int Age { get; set; }
        }

        private Foo Model { get; set; }

        /// <summary>
        ///
        /// </summary>
        protected override void OnInitialized()
        {
            base.OnInitialized();

            Model = new Foo() { Name = "张三", Age = 20 };
        }

        private RenderFragment AutoGenerateTemplate<TModel>(TModel model) => builder =>
        {
            var properties = typeof(TModel).GetProperties().Where(p => p.CanWrite);
            foreach (var property in properties)
            {
                var fieldType = property.PropertyType;
                var fieldName = property.Name;

                // FieldValue
                var valueInvoker = LambdaExtensions.GetPropertyValueLambda<TModel, object>(model, fieldName).Compile();
                var fieldValue = valueInvoker.Invoke(model);

                // ValueChanged
                var valueChangedInvoker = CreateLambda<TModel>(fieldType).Compile();
                var fieldValueChanged = valueChangedInvoker(model, fieldName);

                // ValueExpression
                var body = Expression.Property(Expression.Constant(model), typeof(TModel), fieldName);
                var tDelegate = typeof(Func<>).MakeGenericType(fieldType);
                var valueExpression = Expression.Lambda(tDelegate, body);

                var componentType = fieldType.Name switch
                {
                    nameof(Int32) => typeof(BootstrapInputNumber<int>),
                    //_ => typeof(InputText)
                    _ => typeof(BootstrapInput<string>)
                };
                builder.OpenComponent(0, componentType);
                builder.AddAttribute(1, "Value", fieldValue);
                builder.AddAttribute(2, "ValueChanged", fieldValueChanged);
                builder.AddAttribute(3, "ValueExpression", valueExpression);
                builder.CloseComponent();
            }
        };

        private Expression<Func<TModel, string, object>> CreateLambda<TModel>(Type fieldType)
        {
            var exp_p1 = Expression.Parameter(typeof(TModel));
            var exp_p2 = Expression.Parameter(typeof(string));
            var method = GetType().GetMethod(nameof(CreateCallback), BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(fieldType, typeof(TModel));
            var body = Expression.Call(Expression.Constant(this), method, exp_p1, exp_p2);

            return Expression.Lambda<Func<TModel, string, object>>(Expression.Convert(body, typeof(object)), exp_p1, exp_p2);
        }

        private EventCallback<TType> CreateCallback<TType, TModel>(TModel model, string fieldName) => Microsoft.AspNetCore.Components.EventCallback.Factory.Create<TType>(this, t =>
        {
            if (model != null)
            {
                var invoker = LambdaExtensions.SetPropertyValueLambda<TModel, object>(model, fieldName).Compile();
                invoker.Invoke(model, t);
            }
        });
    }
}
